#include "main.hpp"

using namespace std;


namespace
{
	const int32 framePeriod = 16;
	int32 mainWindow;
	b2World* world;
	float32 settingsHz = 60.0;
	int32 velocityIterations = 8;
	int32 positionIterations = 3; 
	int32 width = 640;
	int32 height = 480;
	//GLUI *glui;
	DebugDraw m_DebugDraw;
	ContactListener m_ContactListener;
	const float32 desiredVel = 1.0f;
	const b2Vec2 vel(0.4f, 0.4f);
	b2Vec2 viewCenter(0.0f, 20.0f);
	float32 viewZoom = 1.0f;
	int tx, ty, tw, th;
    std::map<std::string, usrdata_t*> objs;
    float32 MAX_VEL = 8.0f;    
    const unsigned MAX_KEYS = 300;
    bool keys[MAX_KEYS];
    float32 timeStep = 0.0f;
}

	CollisionedObjects_t CollisionedObjects;

static void Exit(int code)
{
	
#ifdef FREEGLUT
	glutLeaveMainLoop();
#endif
	exit(code);
}


static void Resize(int32 w, int32 h)
{
	width = w;
	height = h;
    
	GLUI_Master.get_viewport_area(&tx, &ty, &tw, &th);
	glViewport(tx, ty, tw, th);
    
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	float32 ratio = float32(tw) / float32(th);
    
	b2Vec2 extents(ratio * 25.0f, 25.0f);
	extents *= viewZoom;
    
	b2Vec2 lower = viewCenter - extents;
	b2Vec2 upper = viewCenter + extents;
    
	// L/R/B/T
	gluOrtho2D(lower.x, upper.x, lower.y, upper.y);
}

static void Timer(int)
{
	glutSetWindow(mainWindow);
	glutPostRedisplay();
	glutTimerFunc(framePeriod, Timer, 0);
}

static b2Vec2 ConvertScreenToWorld(int32 x, int32 y)
{
	float32 u = x / float32(tw);
	float32 v = (th - y) / float32(th);
    
	float32 ratio = float32(tw) / float32(th);
	b2Vec2 extents(ratio * 25.0f, 25.0f);
	extents *= viewZoom;
    
	b2Vec2 lower = viewCenter - extents;
	b2Vec2 upper = viewCenter + extents;
    
	b2Vec2 p;
	p.x = (1.0f - u) * lower.x + u * upper.x;
	p.y = (1.0f - v) * lower.y + v * upper.y;
	return p;
}



static void Keyboard(unsigned char key, int x, int y)
{
    B2_NOT_USED(x);
	B2_NOT_USED(y); 
	switch (key)
	{
        case 27:
#ifndef __APPLE__
            // freeglut specific function
            glutLeaveMainLoop();
#endif
            Exit(0);
            break;
            
        case 32:
            keys[32] = true;
            break;
            
    }
}

static void releaseKey(int key, int x, int y)
{
    
	switch (key)
    {
		case GLUT_KEY_LEFT :
            keys[GLUT_KEY_LEFT] = false;
            break;

		case GLUT_KEY_RIGHT :
            keys[GLUT_KEY_RIGHT] = false;
            break;
		
        case GLUT_KEY_UP :
            keys[GLUT_KEY_UP] = false;
            break;
            
		case GLUT_KEY_DOWN :
            keys[GLUT_KEY_DOWN] = false;
            break;
                    
        case 32:
            keys[32] = false;
            break;
	}
}

static void KeyboardSpecial(int key, int x, int y)
{
	B2_NOT_USED(x);
	B2_NOT_USED(y);
    switch (key)
    {
        case GLUT_KEY_LEFT:
            keys[GLUT_KEY_LEFT] = true;
            break;
        case GLUT_KEY_RIGHT:
            keys[GLUT_KEY_RIGHT] = true;
            break;
        case GLUT_KEY_DOWN:
            keys[GLUT_KEY_DOWN] = true;
            break;
        case GLUT_KEY_UP:
            keys[GLUT_KEY_UP] = true;
            break;        
    }
}

static void Acction()
{
    b2Body* hero = NULL;
    b2Body* bullet = NULL;
    float32 posX = 0.0f, velX = 0.0f, posY = 0.0f, velY = 0.0f;
    static bool isJumping = false;
    
	if(keys[32])
    {
        bullet = getBody("bullet", world);
        if (bullet != NULL)
            world->DestroyBody(bullet);
        hero = getBody("hero", world);
        posX = hero->GetPosition().x;
        posY = hero->GetPosition().y;
        CreateBullet(b2Vec2(posX, posY+2.0f), CUD("bullet", BULLET, NULL, &objs), world);

    }
    else if(keys[GLUT_KEY_LEFT])
    {
        hero = getBody("hero", world);
        velX = hero->GetLinearVelocity().x;
        velY = hero->GetLinearVelocity().y;
        if (abs(velX) > MAX_VEL)
        {
            velX = MAX_VEL * ((velX > 0)?1.0f:-1.0f);
            hero->SetLinearVelocity(b2Vec2(velX, velY));
        }
        if (velX > -MAX_VEL)
            hero->SetLinearVelocity(b2Vec2(-8.0f, 0));

    }
    else if(keys[GLUT_KEY_RIGHT])
    {
        hero = getBody("hero", world);
        velX = hero->GetLinearVelocity().x;
        velY = hero->GetLinearVelocity().y;
        if (abs(velX) > MAX_VEL)
        {
            velX = MAX_VEL * ((velX > 0)?1.0f:-1.0f);
            hero->SetLinearVelocity(b2Vec2(velX, velY));
        }
        if (hero->GetLinearVelocity().x < MAX_VEL)
            hero->SetLinearVelocity(b2Vec2(8.0f, 0));

    }
    else if(keys[GLUT_KEY_DOWN])
    {
    }
    else if(keys[GLUT_KEY_UP] && !isJumping)
    {
        hero = getBody("hero", world);
        /*
        velX = hero->GetLinearVelocity().x;
        velY = hero->GetLinearVelocity().y;
        
        if (abs(velX) > MAX_VEL)
        {
            velX = MAX_VEL * ((velX > 0)?1.0f:-1.0f);
            hero->SetLinearVelocity(b2Vec2(velX, velY));
        }
        if (hero->GetLinearVelocity().x < MAX_VEL)
        */
        velX = hero->GetLinearVelocity().x;
        velX = MAX_VEL * ((velX > 0)?8.0f:-8.0f);
        hero->SetLinearVelocity(b2Vec2(8.0f, 14.0));
        isJumping = true;
    }
    else if (isJumping)
    {
        hero = getBody("hero", world);
        velX = hero->GetLinearVelocity().x;
        velY = hero->GetLinearVelocity().y;
        if (velX == 0 && velY == 0)
            isJumping = false;
    }
    else if(!isJumping)
    {
        hero = getBody("hero", world);
        if(abs(hero->GetLinearVelocity().x) > 0)
            hero->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
    }
}



static void SimulationLoop()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	b2Vec2 oldCenter = viewCenter;
    b2Body* hero = getBody("hero", world);
    if (hero == NULL) return;
    usrdata_t* usr = static_cast<usrdata_t*>(hero->GetUserData());
    enemy_t* enemy = static_cast<enemy_t*>(usr->object);
    
	timeStep = settingsHz > 0.0f ? 1.0f / settingsHz : float32(0.0f);
    //if (enemy != NULL && enemy->gameover)
    //    timeStep = 0.0f;
    
	world->Step(timeStep, velocityIterations, positionIterations);
	if (oldCenter.x != viewCenter.x || oldCenter.y != viewCenter.y)
	{
		Resize(width, height);
	}

    
    char buff[100];	
    //if (enemy != NULL && enemy->gameover)
    //    m_DebugDraw.DrawString(20, 21, "GAME OVER");
    /*
	sprintf(buff, "0 Pos.X : %f Pos.Y : %f Vel.X : %f Vel.Y : %f" , ball0->GetPosition().x, ball0->GetPosition().y,
		ball0->GetLinearVelocity().x, ball0->GetLinearVelocity().y);
    m_DebugDraw.DrawString(20, 21, buff);
    memset(buff, 0, 100);
	b2Body* ball1 = getBody("bola1", world);
	sprintf(buff, "1 Pos.X : %f Pos.Y : %f Vel.X : %f Vel.Y : %f" , ball1->GetPosition().x, ball1->GetPosition().y,
            ball1->GetLinearVelocity().x, ball1->GetLinearVelocity().y);
    
    m_DebugDraw.DrawString(20, 35, buff);
    */
    
    AcctionsPostCollision(CollisionedObjects, &objs, world);
    Acction();
    
	

	world->DrawDebugData();
	glutSwapBuffers();
}


int main(int argc, char** argv)
{
	// Define the gravity vector.
	b2Vec2 gravity(0.0f, -9.78f);
    
	world = new b2World(gravity);	

	uint32 flags = 0;
	flags += 1	* b2Draw::e_shapeBit;
	flags += 1	* b2Draw::e_jointBit;
	flags += 0	* b2Draw::e_aabbBit;
	flags += 0	* b2Draw::e_pairBit;
	flags += 0	* b2Draw::e_centerOfMassBit;
	m_DebugDraw.SetFlags(flags);
	world->SetDebugDraw(&m_DebugDraw);
	world->SetContactListener(&m_ContactListener);
    
	// Define the ground body.    
	CreateRect(b2Vec2(-32.0f, 40.0f), b2Vec2(32.0f, 40.0f), CUD("gndUp", GND_UP, NULL, &objs), world);
	CreateRect(b2Vec2(32.0f, 40.0f), b2Vec2(32.0f, -4.8f), CUD("gndRight", GND_RIGHT, NULL, &objs), world);
	CreateRect(b2Vec2(32.0f, -4.8f), b2Vec2(-32.0f, -4.8f), CUD("gndDown", GND_DOWN, NULL, &objs), world);
	CreateRect(b2Vec2(-32.0f, -4.8f), b2Vec2(-32.0f, 40.0f), CUD("gndLeft", GND_LEFT, NULL, &objs), world);
	
    
	// Balls Enemy
	CreateEnemyBall(b2Vec2(-28.0f, 35.0f), b2Vec2(505.0f, -0.5f), 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, CUD("bola0", CIRCLE_ENEMY, new enemy_t(2, 1.0f), &objs), world);
	CreateEnemyBall(b2Vec2(28.0f, 35.0f), b2Vec2(-505.0f, -0.5f), 2.0f, 1.0f, 1.0f, 0.0f, 0.0f, CUD("bola1", CIRCLE_ENEMY, new enemy_t(4, 2.0f), &objs), world);
	//CreateEnemyBall(b2Vec2(8.0f, 35.0f), b2Vec2(-505.0f, -0.5f), 2.0f, 1.0f, 1.0f, 0.0f, 0.0f, CUD("bola2", CIRCLE_ENEMY, new enemy_t(4, 2.0f), &objs), world);
    
    // Box Enemy
	CreateEnemyBox(b2Vec2(14.0f, 35.0f), b2Vec2(505.0f, -0.5f), b2Vec2(1.5f, 1.5f), 1.0f, 1.0f, 0.3f, 0.0f, CUD("boxE0", BOX_ENEMY, new enemyPoly_t(4, b2Vec2(4.0f, 4.0f)), &objs), world);
    

	// Rectangulo
	CreateBox( -20.0f, 20.0f, 1.0f, 0.5f, CUD("caja0", BOX, NULL, &objs), world);
	CreateBox( 10.0f, 20.0f, 15.0f, 0.5f, CUD("caja1", BOX, NULL, &objs), world);

	// Hero
	CreateDynBox( -0.5f, -3.0f, 1.0f, 1.0f, 0.45f, 1.0f, 0.25f, CUD("hero", DYNBOX, new enemy_t(2, 1.0f), &objs), world);
    //void CreatePlayer(initPos, initDir, radius, density, restitution, friction, gravity, ballUD, lWorld)
    //CreatePlayer(b2Vec2(-0.5f, -2.0f), b2Vec2(0.0f, 0.0f), 0.7f, 1.0f, 0.0f, 0.0f, 0.0f, CUD("hero", DYNBOX, NULL, &objs), world);

         
    glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	glutInitWindowSize(width, height);
	mainWindow = glutCreateWindow("Re-Pang Test1");
	glutDisplayFunc(SimulationLoop);
	GLUI_Master.set_glutReshapeFunc(Resize);
    GLUI_Master.set_glutKeyboardFunc(Keyboard);
    GLUI_Master.set_glutSpecialFunc(KeyboardSpecial);

    glutIgnoreKeyRepeat(1);
    glutSpecialUpFunc(releaseKey);
    
	glutTimerFunc(framePeriod, Timer, 0);
	glutSetWindow(mainWindow);
	glutMainLoop();
    
    DestroyAllObjects(&objs);
            
	return 0;
}
